package com.shigen.file.util;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.shigen.file.entity.CityInfo;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.springframework.core.io.ClassPathResource;

/**
 * IP地址解析工具类
 *
 * @author shigenfu
 */
@Slf4j
public class IPUtil {

  private static final String IP_127 = "127.0.0.1";
  private static final String IP_0_0 = "0:0:0:0:0:0:0:1";

  /**
   * 通过request获得ip
   *
   * @param request
   * @return
   */
  public static String getIp(HttpServletRequest request) {
    // 获取X-Forwarded-For
    String ip = request.getHeader("x-forwarded-for");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      // 获取Proxy-Client-IP
      ip = request.getHeader("Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      // WL-Proxy-Client-IP
      ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      // 获取的IP实际上是代理服务器的地址，并不是客户端的IP地址
      ip = request.getRemoteAddr();
      if (IP_127.equals(ip) || IP_0_0.equals(ip)) {
        // 根据网卡取本机配置的IP
        try {
          ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
          log.error("get local host exception {}", e.getMessage());
        }
      }
    }
    // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
    if (ip != null && ip.length() > 15) {
      if (ip.indexOf(",") > 0) {
        ip = ip.substring(0, ip.indexOf(","));
      }
    }
    return ip;
  }

  /**
   * 获得本机ip
   *
   * @return
   */
  public static String localIp() {
    String localIp = null;
    try {
      localIp = InetAddress.getLocalHost().getHostAddress();
    } catch (UnknownHostException e) {
      log.error("get local host exception {}", e.getMessage());
    }
    return localIp;
  }

  /**
   * 校验ip
   *
   * @param ip
   * @return
   */
  public static boolean isIp(String ip) {
    if (ip.length() < 7 || ip.length() > 15) {
      return false;
    }
    String regex = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
    Pattern pat = Pattern.compile(regex);
    Matcher mat = pat.matcher(ip);
    return mat.find();
  }

  /**
   * 根据ip获得城市信息
   *
   * @param ip
   * @return
   * @throws IOException
   */
  public static String getCityInfo(String ip) throws IOException {
    if (!Util.isIpAddress(ip)) {
      System.out.println("Error: Invalid ip address");
    }

    // 获取ip库路径
    ClassPathResource classPathResource = new ClassPathResource("ip2region.db");
    if (classPathResource.getClassLoader() == null) {
      log.error("存储路径发生错误，没有被发现");
      return null;
    }
    InputStream inputStream = classPathResource.getInputStream();
    byte[] bytes = IoUtil.readBytes(inputStream);

    // 查询算法：B-tree、Binary、Memory
    int algorithm = DbSearcher.MEMORY_ALGORITYM;
    try {
      DbSearcher searcher = new DbSearcher(new DbConfig(), bytes);
      String value = AlgorithmEnum.getMethodByAlgorithm(algorithm);
      if (StrUtil.isEmpty(value)) {
        return null;
      }

      Method method = searcher.getClass().getMethod(value, String.class);
      DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);
      return dataBlock.getRegion();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * 根据IP地址获得城市信息
   *
   * @param ip
   * @return
   */
  public static String getCityInfoStr(String ip) {
    String cityInfo = null;
    try {
      cityInfo = getCityInfo(ip);
    } catch (IOException e) {
      e.printStackTrace();
    }
    if (StrUtil.isEmpty(cityInfo)) {
      return "";
    }
    String[] split = cityInfo.split("\\|");
    CityInfo info = new CityInfo()
        .setNation(split[0])
        .setProvince(split[2])
        .setCity(split[3])
        .setOperator(split[4]);
    return info.toString();
  }

  public enum AlgorithmEnum {
    /**
     * btreeSearch
     */
    B_TREE_SEARCH(DbSearcher.BTREE_ALGORITHM, "btreeSearch"),
    /**
     * binarySearch
     */
    BINARY_SEARCH(DbSearcher.BINARY_ALGORITHM, "binarySearch"),
    /**
     * memorySearch
     */
    MEMORY_SEARCH(DbSearcher.MEMORY_ALGORITYM, "memorySearch");

    /**
     * 算法类型
     */
    private final int algorithm;
    /**
     * 使用算法名称
     */
    private final String value;

    public static String getMethodByAlgorithm(int algorithm) {
      for (AlgorithmEnum algorithmEnum : AlgorithmEnum.values()) {
        if (algorithmEnum.algorithm == algorithm) {
          return algorithmEnum.value;
        }
      }
      return null;
    }

    AlgorithmEnum(int algorithm, String value) {
      this.algorithm = algorithm;
      this.value = value;
    }
  }

}
